package com.powergisol.core.math;

import java.util.ArrayList;
import java.util.List;

public class Plane {

    /**
     * 面的端点
     */
    private List<Dot> dots;

    /**
     * 面的边线
     */
    private List<Line> lines = new ArrayList<>();

    /**
     * 平面法向量
     */
    private Vector normalVector;
    /**
     * 平面方程 a*x+b*y+c*z+d = 0 的4个系数
     */
    private double a;
    private double b;
    private double c;
    private double d;

    public Plane(List<Dot> dots) {
        this.dots = dots;
        init(dots);
    }

    private void init(List<Dot> dots) {

        if (!dots.get(0).equals(dots.get(dots.size() - 1))) {
            //判断最后的端点是否闭合
            throw new RuntimeException("多边形必须闭合,最后1个点必须和第1个点相等");
        }

        if (dots.isEmpty() || dots.size() < 4) {

            throw new RuntimeException("多边形的点必须3个以上");
        }

        this.dots = dots;

        for (int i = 0; i < dots.size() - 1; i++) {
            Dot dotA = dots.get(i);
            Dot dotB = dots.get(i + 1);

            Line line = new Line(dotA, dotB);
            lines.add(line);
        }

        Vector one = lines.get(0).getDirectionVector();
        Vector two = lines.get(1).getDirectionVector();

        /**
         * 根据两个方向向量求法向量,也就是求叉乘
         */

        this.normalVector = one.crossProduct(two);

        /**
         * 根据点平面定义规则,法向量为(a,b,c)
         */
        this.a = this.normalVector.getX();
        this.b = this.normalVector.getY();
        this.c = this.normalVector.getZ();

        /**
         * 平面方程 a*x+b*y+c*z+d = 0
         * 经过点其中一点
         */

        Dot dot = dots.get(0);
        d = -(a * dot.getX() + b * dot.getY() + c * dot.getZ());
    }

    /**
     * http://netedu.xauat.edu.cn/jpkc/netedu/jpkc/gdsx/homepage/5jxsd/51/513/5307/530705.htm
     * https://baike.baidu.com/item/%E7%82%B9%E5%88%B0%E5%B9%B3%E9%9D%A2%E8%B7%9D%E7%A6%BB#1
     * <p>
     * 面外一点到平面的距离
     *
     * @param dot
     * @return
     */
    public double distance(Dot dot) {

        double x1, y1, z1;
        x1 = dot.getX();
        y1 = dot.getY();
        z1 = dot.getZ();

        double distance = Math.abs(a * x1 + b * y1 + c * z1 + d) / Math.sqrt(a * a + b * b + c * c);
        /**
         * 如果满足点在平面上,则(a*x1+b*y1+c*z1+d)=0,则距离也为0
         */
        return distance;
    }

    /**
     * https://blog.csdn.net/u013467442/article/details/39183727
     * 求直线与面的交点 注意 如果是线段,需要判断最后返回的点是否在直线上
     * @param line
     * @return
     */
    public Dot getIntersection(Line line) {

        double v1, v2, v3;// 直线方向向量
        double m1, m2, m3; //直线经过的1点
        double n1, n2, n3; //平面经过点n
        double vp1, vp2, vp3; //法向量vp

        v1 = line.getDirectionVector().getX();
        v2 = line.getDirectionVector().getY();
        v3 = line.getDirectionVector().getZ();

        m1 = line.getM().getX();
        m2 = line.getM().getY();
        m3 = line.getM().getZ();

        n1 = this.dots.get(0).getX();
        n2 = this.dots.get(0).getY();
        n3 = this.dots.get(0).getZ();

        vp1 = this.normalVector.getX();
        vp2 = this.normalVector.getY();
        vp3 = this.normalVector.getZ();

        double t = ((n1 - m1) * vp1 + (n2 - m2) * vp2 + (n3 - m3) * vp3) / (vp1 * v1 + vp2 * v2 + vp3 * v3);
        double x = m1+ v1 * t;
        double y = m2+ v2 * t;
        double z = m3+ v3 * t;

        return new Dot(x,y,z);
    }

    public List<Line> getLines() {
        return lines;
    }

    public List<Dot> getDots() {
        return dots;
    }
}
